import { isRef, reactive, toRefs, watch, type Ref } from 'vue';
import type { AxiosRequestConfig, Method, ApiResponseSuccess, ApiResponseError } from 'axios';
import type { ApiFuncData, RequestFunction } from '@/apis/types';

export type TRequestData = {
  params?: AxiosRequestConfig['params'];
  data?: any;
  query?: any;
  method?: Method;
};

export type Immediate = boolean | (() => any);
/**
 * 请求的统一方法
 * @param requestFunc 请求的方法或者请求配置参数
 * @param data requestFunc的ApiFuncData类型请求数据。如果是响应式对象，会自动监听data的变化，重新发送请求，data.value变为undefined时不发送请求，并清空res
 * @param immediate 是否立即执行请求，如果提供的是函数，则会在初始化时requestFunc之前运行
 * @returns
 */
export function useRequest<T, D = any, Q = any>(
  requestFunc: RequestFunction<T, D, Q>,
  data?: Ref<ApiFuncData<D, Q> | undefined> | ApiFuncData<D, Q>,
  immediate: Immediate = true,
) {
  const config: {
    /** 请求加载状态 */
    loading: boolean;
    /** 请求成功的返回结果 */
    res: ApiResponseSuccess<T> | null;
    /** 请求失败的返回结果 */
    errRes: ApiResponseError | null;
    /** 第一次加载结束标志 */
    init: boolean;
  } = reactive({
    loading: false,
    res: null,
    errRes: null,
    init: false,
  });

  /** 包装后的请求方法 */
  async function requestData() {
    config.loading = true;
    if (config.init === false && typeof immediate === 'function') {
      await immediate();
      config.init = true;
    }

    try {
      const _data = unref(data);
      // 如果是响应式对象，data.value变为undefined时不发送请求，并清空res
      if (isRef(data) && _data === undefined) {
        config.loading = false;
        config.res = null;
        config.errRes = null;
        return;
      }
      const response = await requestFunc!(_data!);
      if (response.success) {
        config.res = response;
        config.errRes = null;
      } else {
        config.errRes = response as unknown as ApiResponseError;
        config.res = null;
      }
    } catch (e: any) {
      config.errRes = e.data as ApiResponseError;
      config.res = null;
      console.error('请求函数执行错误', e);
    }
    // config.res = response;
    config.loading = false;

    return config.res;
  }

  // immediate && requestData();
  // 如果是响应式对象，设置监听器，值变化了以后自动更新
  if (isRef(data)) {
    watch(data, requestData, { immediate: immediate !== false });
  } else {
    watch(() => data, requestData, { immediate: immediate !== false });
  }

  return {
    ...toRefs(config),
    /** 包装后的请求方法
     * @description 该函数没有参数.如果需要在数据改变时，重新发送请求，data 请使用响应式数据，
     */
    request: requestData,
  };
}
